﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BuildOptimizer
{
    class BoGameState
    {
        public BoGameState()
        {
            initializeUnits();
            initializeBuildings();
            initializeUpgrades();
        }
        private BoGameState(BoGameState gameState)
        {
            _mineralsMinedTotal = gameState._mineralsMinedTotal;
            _gasMinedTotal = gameState._gasMinedTotal;
            _mineralsAvailable = gameState._mineralsAvailable;
            _gasAvailable = gameState._gasAvailable;

            for (int i = 0; i < gameState._units.Length; i++)
            {
                _units[i] = gameState._units[i];
                _unitsBeingBuilt[i] = gameState._unitsBeingBuilt[i];
            }

            for (int i = 0; i < gameState._buildings.Length; i++)
            {
                _buildings[i] = gameState._buildings[i];
                _buildingsBeingBuilt[i] = gameState._buildingsBeingBuilt[i];
            }

            for (int i = 0; i < gameState._upgrades.Length; i++)
            {
                _upgrades[i] = gameState._upgrades[i];
                _upgradesBeingBuilt[i] = gameState._upgradesBeingBuilt[i];
            }

            _nbBasesOccupied = gameState._nbBasesOccupied;

            for (int i = 0; i < gameState._hatcheries.Count; i++)
            {
                _hatcheries.Add(gameState._hatcheries[i].clone());
            }

            for (int i = 0; i < gameState._queens.Count; i++)
            {
                _queens.Add(gameState._queens[i].clone());
            }

            for (int i = 0; i < gameState._tasks.Count; i++)
            {
                _tasks.Add(gameState._tasks[i].clone());
            }

            _nbTicksPassed = gameState._nbTicksPassed;
        }
        public BoGameState clone()
        {
            return new BoGameState(this);
        }

        public void setToStartState()
        {
            _nbTicksPassed = 0;
            _mineralsMinedTotal = 0;
            _mineralsAvailable = 50;
            _gasMinedTotal = 0;
            _gasAvailable = 0;

            initializeUnits();
            initializeBuildings();
            initializeUpgrades();

            _hatcheries.Clear();
            _queens.Clear();
            _tasks.Clear();

            _nbBasesOccupied = 0;
            int newBaseIndex = updateNbBases();
            startBuildingBuilding((int)Buildings.Hatchery);
            finishedBuildingBuilding((int)Buildings.Hatchery);
            addHatcheryAtResourcePatch(newBaseIndex);
            _hatcheries[0].setLarvaeOnStartHatchery();

            _units[(int)Units.Drone] = 6;
            _units[(int)Units.Overlord] = 1;

            for (int i = 0; i < 6; i++)
            {
                getHatcheryAtResources(0).moveDroneToMinerals();
            }
        }

        //minerals and gas
        private float _mineralsMinedTotal = 0;
        private float _gasMinedTotal = 0;
        private float _mineralsAvailable = 0;
        private float _gasAvailable = 0;

        public float mineralsAvailable
        {
            get
            {
                return _mineralsAvailable;
            }
        }
        public float gasAvailable
        {
            get
            {
                return _gasAvailable;
            }
        }
        public float mineralsMinedTotal
        {
            get
            {
                return _mineralsMinedTotal;
            }
        }
        public float gasMinedTotal
        {
            get
            {
                return _gasMinedTotal;
            }
        }
        public void removeMineralsAndGas(float minerals, float gas)
        {
            _mineralsAvailable -= minerals;
            _gasAvailable -= gas;
        }
        public void reimburseMineralsAndGas(float minerals, float gas)
        {
            _mineralsAvailable += minerals;
            _gasAvailable += gas;
        }

        //supply
        public float supplyUsed
        {
            get
            {
                float supplyUsed = 0.0f;
                supplyUsed += (1.0f * _unitsBeingBuilt[(int)Units.Drone]);
                supplyUsed += (0.5f * _unitsBeingBuilt[(int)Units.Zergling]);
                supplyUsed += (0.5f * _unitsBeingBuilt[(int)Units.Baneling]);
                supplyUsed += (2.0f * _unitsBeingBuilt[(int)Units.Roach]);
                supplyUsed += (2.0f * _unitsBeingBuilt[(int)Units.Mutalisk]);
                supplyUsed += (2.0f * _unitsBeingBuilt[(int)Units.Infestor]);
                supplyUsed += (2.0f * _unitsBeingBuilt[(int)Units.Queen]);
                supplyUsed += (2.0f * _unitsBeingBuilt[(int)Units.Hydralisk]);
                supplyUsed += (2.0f * _unitsBeingBuilt[(int)Units.Corruptor]);
                supplyUsed += (6.0f * _unitsBeingBuilt[(int)Units.Ultralisk]);
                supplyUsed += (4.0f * _unitsBeingBuilt[(int)Units.Broodlord]);

                supplyUsed += (1.0f * _units[(int)Units.Drone]);
                supplyUsed += (0.5f * _units[(int)Units.Zergling]);
                supplyUsed += (0.5f * _units[(int)Units.Baneling]);
                supplyUsed += (2.0f * _units[(int)Units.Roach]);
                supplyUsed += (2.0f * _units[(int)Units.Mutalisk]);
                supplyUsed += (2.0f * _units[(int)Units.Infestor]);
                supplyUsed += (2.0f * _units[(int)Units.Queen]);
                supplyUsed += (2.0f * _units[(int)Units.Hydralisk]);
                supplyUsed += (2.0f * _units[(int)Units.Corruptor]);
                supplyUsed += (6.0f * _units[(int)Units.Ultralisk]);
                supplyUsed += (4.0f * _units[(int)Units.Broodlord]);
                return supplyUsed;
            }
        }
        public float supplyAvailable
        {
            get
            {
                float supplyAvailable = 0.0f;
                supplyAvailable += (8.0f * _units[(int)Units.Overlord]);
                supplyAvailable += (8.0f * _unitsBeingBuilt[(int)Units.Overseer]);
                supplyAvailable += (8.0f * _units[(int)Units.Overseer]);
                supplyAvailable += (2.0f * _hatcheries.Count);
                supplyAvailable = (supplyAvailable > 200.0f) ? 200.0f : supplyAvailable;
                return supplyAvailable;
            }
        }
        public float supplyLeft
        {
            get
            {
                return (supplyAvailable - supplyUsed);
            }
        }

        //larvae
        public int nbLarvaeAvailable
        {
            get
            {
                int nbLarvae = 0;
                for (int i = 0; i < _hatcheries.Count; i++)
                {
                    nbLarvae += _hatcheries[i].nbLarvae;
                }
                return nbLarvae;
            }
        }

        //constraint checks
        public bool isResourcesAvailable(float minerals, float gas, float supply)
        {
            if (_mineralsAvailable < minerals)
                return false;

            if (_gasAvailable < gas)
                return false;

            if (supplyLeft < supply)
                return false;

            return true;
        }
        public int nbUnitsBeingBuilt(int unitIndex)
        {
            return _unitsBeingBuilt[unitIndex];
        }
        public int nbUnitsBuilt(int unitIndex)
        {
            return _units[unitIndex];
        }
        public int nbBuildingsBeingBuilt(int buildingIndex)
        {
            return _buildingsBeingBuilt[buildingIndex];
        }
        public int nbBuildingsBuilt(int buildingIndex)
        {
            return _buildings[buildingIndex];
        }
        public bool isUpgradeBeingBuilt(int upgradeIndex)
        {
            return _upgradesBeingBuilt[upgradeIndex];
        }
        public bool isUpgradeBuilt(int upgradeIndex)
        {
            return _upgrades[upgradeIndex];
        }

        //units
        private int[] _units = new int[Enum.GetNames(typeof(Units)).Length];
        private int[] _unitsBeingBuilt = new int[Enum.GetNames(typeof(Units)).Length];
        private void initializeUnits()
        {
            for (int i = 0; i < _units.Length; i++)
            {
                _units[i] = 0;
            }
            for (int i = 0; i < _unitsBeingBuilt.Length; i++)
            {
                _unitsBeingBuilt[i] = 0;
            }
        }
        public void startBuildingUnit(int unitIndex)
        {
            _unitsBeingBuilt[unitIndex]++;
        }
        public void finishedBuildingUnit(int unitIndex)
        {
            _unitsBeingBuilt[unitIndex]--;
            _units[unitIndex]++;
        }
        public void removeUnit(int unitIndex)
        {
            _units[unitIndex]--;
        }

        //buildings
        private int[] _buildings = new int[Enum.GetNames(typeof(Buildings)).Length];
        private int[] _buildingsBeingBuilt = new int[Enum.GetNames(typeof(Buildings)).Length];
        private void initializeBuildings()
        {
            for (int i = 0; i < _buildings.Length; i++)
            {
                _buildings[i] = 0;
            }
            for (int i = 0; i < _buildingsBeingBuilt.Length; i++)
            {
                _buildingsBeingBuilt[i] = 0;
            }
        }
        public void startBuildingBuilding(int buildingIndex)
        {
            _buildingsBeingBuilt[buildingIndex]++;
        }
        public void finishedBuildingBuilding(int buildingIndex)
        {
            _buildingsBeingBuilt[buildingIndex]--;
            _buildings[buildingIndex]++;
        }
        public void removeBuilding(int buildingIndex)
        {
            _buildings[buildingIndex]--;
        }

        //upgrades
        private bool[] _upgrades = new bool[Enum.GetNames(typeof(Upgrades)).Length];
        private bool[] _upgradesBeingBuilt = new bool[Enum.GetNames(typeof(Upgrades)).Length];
        private void initializeUpgrades()
        {
            for (int i = 0; i < _upgrades.Length; i++)
            {
                _upgrades[i] = false;
            }
            for (int i = 0; i < _upgradesBeingBuilt.Length; i++)
            {
                _upgradesBeingBuilt[i] = false;
            }
        }
        public void startBuildingUpgrade(int upgradeIndex)
        {
            _upgradesBeingBuilt[upgradeIndex] = true;
        }
        public void finishedBuildingUpgrade(int upgradeIndex)
        {
            _upgradesBeingBuilt[upgradeIndex] = false;
            _upgrades[upgradeIndex] = true;
        }

        //hatchery management
        private int _nbBasesOccupied = 0;
        public int nbBasesOccupied
        {
            get
            {
                return _nbBasesOccupied;
            }
        }
        public int updateNbBases()
        {
            _nbBasesOccupied++;
            return (_nbBasesOccupied - 1);
        }

        private List<BoHatchery> _hatcheries = new List<BoHatchery>();
        public int nbHatcheries
        {
            get
            {
                return _hatcheries.Count;
            }
        }
        public int nbHatcheriesAtResources
        {
            get
            {
                int nbHatcheriesAtResources = 0;
                for (int i = 0; i < _hatcheries.Count; i++)
                {
                    nbHatcheriesAtResources += (_hatcheries[i].isAtResources) ? 1 : 0;
                }
                return nbHatcheriesAtResources;
            }
        }
        public BoHatchery getHatchery(int hatchIndex)
        {
            return _hatcheries[hatchIndex];
        }
        public BoHatchery getHatcheryAtResources(int baseIndex)
        {
            for (int i = 0; i < _hatcheries.Count; i++)
            {
                if (_hatcheries[i].isAtResources && _hatcheries[i].baseIndex == baseIndex)
                {
                    return _hatcheries[i];
                }
            }
            return null;
        }
        public void addHatcheryInBase(int baseIndex)
        {
            BoHatchery h = new BoHatchery(baseIndex, false);
            _hatcheries.Add(h);
        }
        public void addHatcheryAtResourcePatch(int baseIndex)
        {
            BoHatchery h = new BoHatchery(baseIndex, true);
            _hatcheries.Add(h);
        }

        //queens management
        private List<BoQueen> _queens = new List<BoQueen>();
        public BoQueen getQueen(int queenIndex)
        {
            return _queens[queenIndex];
        }
        public void addQueen()
        {
            BoQueen q = new BoQueen();
            _queens.Add(q);
        }
        public int nbQueens
        {
            get
            {
                return _queens.Count;
            }
        }

        //task management
        private List<BoTask> _tasks = new List<BoTask>();
        public void addTask(BoTask task)
        {
            _tasks.Add(task);
        }

        //updates each second
        private int _nbTicksPassed = 0;
        public int nbTicksPassed
        {
            get
            {
                return _nbTicksPassed;
            }
        }
        private void updateLarvaeSpawning()
        {
            for (int i = 0; i < _hatcheries.Count; i++)
            {
                _hatcheries[i].updateSpawnLarvae();
            }
        }
        private void updateMineralsAndGas()
        {
            for (int i = 0; i < _hatcheries.Count; i++)
            {
                if (_hatcheries[i].isAtResources)
                {
                    float newMineralsMined = _hatcheries[i].newMineralsMined();
                    _mineralsAvailable += newMineralsMined;
                    _mineralsMinedTotal += newMineralsMined;

                    float newGasMined = _hatcheries[i].newGasMined();
                    _gasAvailable += newGasMined;
                    _gasMinedTotal += newGasMined;
                }
            }
        }
        private void updateQueensEnergy()
        {
            for (int i = 0; i < _queens.Count; i++)
            {
                _queens[i].updateEnergy();
            }
        }
        private void updateTasks(BoDecisionNode n)
        {
            for (int i = (_tasks.Count - 1); i >= 0; i--)
            {
                if (_tasks[i].isExecuted(n))
                {
                    _tasks.RemoveAt(i);
                }
            }
        }
        public void tick(BoDecisionNode n)
        {
            _nbTicksPassed++;
            updateLarvaeSpawning();
            updateMineralsAndGas();
            updateQueensEnergy();
            updateTasks(n);
        }
    }
}
